/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * bocciawii
 * Copyright (C) Grupo de trabajo ULE 2010 <ulewii@googlegroups.com>
 * 
 * bocciawii is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * bocciawii is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "partida.h"



Partida::Partida(opciones_aplicacion* opciones){
	log = new ErrorLog(APP_NAME, APP_VERSION, "Partida.txt");
	// Asignamos opciones
	_opciones = opciones;

	// Inicializar render e input, y resto de variables a valores iniciales
	render = new Render(opciones);
	render->IniciaPantalla();
	render->InitSDLGL();
	input = new Input(render->X(), render->Y());

	menu = NULL;
	hud = NULL;

	num_estacionarios = 0;


	// Contadores
	tiempo_pasado = 0;
	hud_ocultado = 0;

	// Baremos de tiempo
	frecuencia = 16;
	espera_mostrar_hud = frecuencia * 10;

	// Ticks para el bucle global
	antes = SDL_GetTicks();
	ahora = 0;

	// Nuestro contador global
	delta = 0;

	// Fuentes
	std::string ruta = RUTA_FUENTES;
	font = new FTGLPixmapFont ((ruta + "Vera.ttf").c_str());
	font->FaceSize(14);

	fuente_hud = new FTGLPixmapFont ((ruta + "Vera.ttf").c_str());
	fuente_hud->FaceSize(_opciones->tam_letra);

	// Cargar el entorno
	CargaEntorno();

	// Cargar el cursor
	IniciaCursorMirada();

	// Cargar la interfaz 2D
	_boxActivoMenu = 0; // Para saber el menú de qué box estamos utilizando.
	menu_ppal = CargaMenuPpal();
	menu_ingame = CrearMenuInGame ();
	selbola = CrearMenuSeleccionBola ();
	selbox = CrearMenuSeleccionBox ();
	espera_parcial = CrearMenuEsperaParciales();
	espera_final = CrearMenuEsperaFinal();
	hud_ppal = CargaHUD(_opciones->tam_letra);
	desconexion = CrearMenuEsperaConexion();
	incorrecto = CrearMenuLanzamientoIncorrecto();

	// Cargamos el estado inicial del juego
	save_state ss = EstadoCero ();
	ss.menu = menu_ppal;
	ss.estado = EST_MENU;
	ss.camera_default = CAMARA_ESTADIO;
	pila_estados.push(ss);
	CambiaEstado();


	// Iniciar equipos y bolas
	equipo_azul = NULL;
	equipo_rojo = NULL;
	equipo_que_lanza = NULL;
	conjunto_bolas = new Bolas();

	// Por último arrancamos el hilo de poll al mando
	input->Iniciar();

	// Y a jugar
	Bucle();

	this->~Partida();


}

Partida::~Partida()
{
	// Destruimos input (detener el poll al mando y limpiar estructuras wiiuse)
	input->~Input();

	// Destruimos contextos de dibujado.
	render->~Render();

	// Destruimos el entorno (liberar texturas y display lists)
	for (int i = num_estacionarios -1 ; i > -1; i--)
	{
		if (estacionario[i]->GetID().find("copia") == std::string::npos)
			estacionario[i]->~ElementoEstacionario();
		else
			free (estacionario[i]);
	}

	// Destruimos los equipos (borrar referencias a bolas, borrar arrays ?)
	if (equipo_rojo != NULL) // No destruir equipo_que_lanza (alguno de los dos será)
		equipo_rojo->~Equipo();
	if (equipo_azul != NULL)
		equipo_azul->~Equipo();

	// Destruimos las bolas (Análogo a los destructores de elemento estacionario)
	conjunto_bolas->~Bolas();

	// Destruimos todos los menús (_layouts)
	menu_ppal->~ScreenLayout();
	menu_ingame->~ScreenLayout();
	selbola->~ScreenLayout();
	selbox->~ScreenLayout();
	espera_parcial->~ScreenLayout();
	espera_final->~ScreenLayout();
	desconexion->~ScreenLayout();
	incorrecto->~ScreenLayout();
	hud_ppal->~ScreenLayout();

	// Destruimos las fuentes
	font->~FTGLPixmapFont();
	fuente_hud->~FTGLPixmapFont();


	// Destruimos las imágenes guardadas 
	glDeleteTextures(1, &_imgBack);
	glDeleteTextures(1, &_imgExit);
	glDeleteTextures(1, &_imgPlay);
	for (int i = 0; i < 6; i++)
		glDeleteTextures(1, &_imgBox[i]);
	glDeleteTextures(1, &_imgBlueTeam);
	glDeleteTextures(1, &_imgRedTeam);
	glDeleteTextures(1, &_imgUndetermined);
	glDeleteTextures(1, &_imgWhiteBall);
	glDeleteTextures(1, &_imgSoftBall);
	glDeleteTextures(1, &_imgSemiBall);
	glDeleteTextures(1, &_imgHardBall);
	for (int i = 0; i < 7; i++)
	{
		glDeleteTextures(1, &_imgLostRed[i]);
		glDeleteTextures(1, &_imgLostBlue[i]);
	}
	glDeleteTextures(1, &_imgPlus);
	glDeleteTextures(1, &_imgMinus);
	for (int i = 0; i < 17; i++)
		glDeleteTextures(1, &cursor_mirada.images[i]);

	//Destruimos el archivo de logs (introducir fin en fichero y cerrar)
	log->~ErrorLog();

	// Desapilamos
	while (!pila_estados.empty())
		pila_estados.pop();


	SDL_Quit();
}



















void Partida::Bucle()
{

	accion_aplicacion evento;
	done = 0;   // Nuestra condición de parada del bucle de la aplicación
	// Quitamos el cursor de SDL, que distrae.
	SDL_ShowCursor(0);

	while (!done)
	{
		ahora = SDL_GetTicks();
		delta = ahora - antes;

		// Comprobación fija, independiente del estado
		if (!input->NumeroConexiones()) // Si no hay wiimotes
			if (menu == NULL)			// y no hay menú
		{							
			save_state ss = pila_estados.top();
			ss.menu = desconexion;
			ss.estado = EST_MENU;
			pila_estados.push(ss);
			CambiaEstado ();		// Añadir el menu
		} else if (menu->GetID() != "espera_conex") // o bien que nuestro menú no sea el 
		{											// de espera de las conexiones
			save_state ss = pila_estados.top();
			ss.menu = desconexion;
			ss.estado = EST_MENU;
			pila_estados.push(ss);
			CambiaEstado ();		// Añadir el menú
		}

		/******************************************************************************
		 * ENTRADA
		 * Procesaremos la entrada siempre y cuando haya pasado un ciclo de juego.
		 * No tener dos acciones entre un frame y otro.
		 */

		if (delta > frecuencia)
		{
			evento = input->Poll(pila_estados.top().estado);		// Hacemos poll

			if (evento.id & ACC_SALIR)								// Procesamos
				done = 1; 

			if (evento.id & ACC_SELECCIONAR)
				ProcesarSeleccion(evento.id_elemento);

			if (evento.id & ACC_MIRAR)
			{
				ProcesarCamara(evento.des);
				hud->SetVisible(0);
				cursor_mirada.visible = 1;
				cursor_mirada.x = evento.pun.x;
				cursor_mirada.y = evento.pun.y;
				SetIndexCursor(evento.des);
				hud_ocultado = SDL_GetTicks();
			} 
			else if (hud != NULL && !hud->GetVisible())			// Especial
			{													// para evitar "temblores"
				long int ahora = SDL_GetTicks();				// de hud y cursor cuando se está mirando
				if (ahora - hud_ocultado > espera_mostrar_hud)
				{
					hud->SetVisible(1);
					cursor_mirada.visible = 0;
				}
			}

		}


		/*****************************************************************
		 * FISICA
		 * Procesar fuerza del mando
		 * Establecer velocidades de bola
		 * Gestionar las colisiones
		 * Dar paso a lógica o bien volver al estado anterior (lanzamiento incorrecto)
		 */
		int saltar_a_logica = 0;

		if (delta > frecuencia)
		{
			conjunto_bolas->GestionarColisiones();
			conjunto_bolas->ActualizarPosicion(delta);
			saltar_a_logica = !conjunto_bolas->EnMovimiento();
		}


		float velocidad;
		float angulo;

		if (pila_estados.top().estado == EST_PREPROCESO_LANZAMIENTO)
		{
			// Tomar las fuerzas del mando
			float acel_y = evento.acel.y;

			if (acel_y > 0)
			{
				velocidad = acel_y + 2.0;
				angulo = 10 + acel_y * 20 / 10;
			}
			else 
			{
				velocidad = 0.0;
				angulo = 10.0;
				acel_y = 0.0;
			}

			if (hud != NULL)
			{
				hud->GetElemByID("fuerza_lanzamiento")->SetPercent(acel_y * 20);
				hud->GetElemByID("potencia")->SetText("Potencia");
			}
		}


		if (pila_estados.top().estado == EST_LANZAMIENTO)
		{
			// Con las fuerzas calculadas antes, 
			//  obtener valores para la velocidad de lanzamiento.





			// Filtrado de lanzamientos erroneos
			if (velocidad == 0)
			{
				std::cerr << "Lanzamiento incorrecto" << std::endl;
				save_state ss = pila_estados.top();
				pila_estados.pop(); // salimos del estado lanzamiento.
				ss.estado = EST_MENU;
				ss.menu = incorrecto;
				pila_estados.push(ss);
				CambiaEstado();
			} 
			else
			{

				bola_lanzada = equipo_que_lanza->GetBolaSeleccionada();
				if (!bola_lanzada->Lanzada())
				{
					float* posicion = render->camaras->GetPosicionBox();
					bola_lanzada->Mover(posicion[0], 0.35, posicion[2]);
					float* direccion = render->camaras->GetDireccion();
					bola_lanzada->Dir(direccion[0], direccion[1], direccion[2]);
					bola_lanzada->Init(velocidad, angulo);
					bola_lanzada->Lanzar();
				}


				if (saltar_a_logica)
				{
					tiempo_pasado += delta;
					if (tiempo_pasado > 1500) {
						// En términos de corrección, esto es para matarme, pero
						// si no lo pongo casi no te da tiempo a ver dónde ha quedao
						// la bola

						tiempo_pasado = 0;

						hud->GetElemByID("fuerza_lanzamiento")->SetPercent(0);
						hud->GetElemByID("potencia")->SetText("Esperando lanzamiento");

						save_state ss = pila_estados.top();

						pila_estados.pop();
						ss.estado = EST_LOGICA;
						pila_estados.push(ss);
						
					}
				} else {
				}
			}
		}

		/*
		 * LÓGICA
		 * Establecer el orden del juego
		 * Realizar acciones con respecto a finales de parcial o de juego
		 * Realizar acciones con respecto a las posiciones de las bolas
		 * Comprobar condiciones especiales del juego
		 */
		if (pila_estados.top().estado == EST_LOGICA)
			ProcesarLogica();











		/*****************************************************************
		 * DIBUJAR
		 * Dibujar la escena si corresponde
		 * Resetear contador de frame
		 */

		// Si han pasado delta milisegundos (aprox 1/100 * 1000), dibujamos
		if (delta > frecuencia)
		{
			DibujarEscena();
			antes = ahora;
		}





	}
}

/*
 *
 *  Operaciones sobre estados
 *
 *
 */


save_state Partida::EstadoCero()
{
	save_state ss;
	ss.estado = 0;
	ss.camera_default = 1;
	ss.box_camera = 1;
	ss.menu = NULL;
	ss.hud = NULL;
	ss.duracion_milis = -1;
	return ss;
}



void Partida::CambiaEstado()
{
	hud = pila_estados.top().hud;
	menu = pila_estados.top().menu;

	if (menu!= NULL)
		menu->SeleccionarDefecto();

	render->camaras->ActivaCamara(pila_estados.top().camera_default, pila_estados.top().box_camera);
	render->camaras->EligeBox(pila_estados.top().box_camera);

	if (pila_estados.top().estado == EST_JUEGO)
	{
		hud->GetElemByID("control_2")->SetText("2: Cámara táctica");
		hud->GetElemByID("control_1")->SetText("1: Cámara de estadio");
		hud->GetElemByID("control_3")->SetText("B: Lanzar");

		hud->Pack(render->X(), render->Y());
	}


	if (pila_estados.top().estado == EST_TACTICA)
	{
		hud->GetElemByID("control_2")->SetText("+/-: Zoom");
		hud->GetElemByID("control_1")->SetText("1: Cámara de estadio");
		hud->GetElemByID("control_3")->SetText("B: Primera persona");

		hud->Pack(render->X(), render->Y());
	}


	if (pila_estados.top().estado == EST_OBSERVACION)
	{
		hud->GetElemByID("control_2")->SetText("2: Cámara táctica");
		hud->GetElemByID("control_1")->SetText("+/-: Zoom");
		hud->GetElemByID("control_3")->SetText("B: Primera persona");

		hud->Pack(render->X(), render->Y());
	}
}